package DataModel.LayoutParser;

import java.awt.Point;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.LinkedList;

import DataModel.CommonLibrary.CommonDefs.Layer;
import DataModel.LayoutManager.CPolygon;
import DataModel.LayoutManager.CRectangle;

/**
 * This class imports layout in CIF format
 * 
 * @author evgeni
 *
 */
public class CIFImporter
{
	
	/***************************************************/
	/****** Constants ************************************/
	/***************************************************/
	
	private static final int BIGSIGNED = ((0X7FFFFFFF-9)/10);  /** max value that can add extra digit */

	// enumerated types for cif 2.0 parser
	private static final int SEMANTICERROR = 0;
	private static final int SYNTAXERROR   = 1;
	private static final int WIRECOM       = 2;
	private static final int BOXCOM        = 3;
	private static final int POLYCOM       = 4;
	private static final int FLASHCOM      = 5;
	private static final int DEFSTART      = 6;
	private static final int DEFEND        = 7;
	private static final int DELETEDEF     = 8;
	private static final int LAYER         = 9;
	private static final int CALLCOM       = 10;
	private static final int COMMENT       = 11;
	private static final int NULLCOMMAND   = 12;
	private static final int USERS         = 13;
	private static final int END           = 14;
	private static final int ENDFILE       = 15;
	private static final int SYMNAME       = 16;
	private static final int INSTNAME      = 17;
	private static final int GEONAME       = 18;
	private static final int LABELCOM      = 19;

	// types for transformations
	private static final int MIRROR      = 100;
	private static final int TRANSLATE   = 101;
	private static final int ROTATE      = 102;

	static class TransformData
	{
		int				   kind;
		boolean            xCoord;
		int                xt, yt;
		int                xRot, yRot;
	};

	static class Symbol
	{
		int symNumber;		/** symbol number for this entry */					
		boolean defined;			
		String name;		/** name of this symbol */								
		
		Symbol(int num)
		{
			symNumber = num;
			defined = false;
			name = null;
		}
	};

	static class FrontLinkedTransform
	{
		TransformData tValue;
		FrontLinkedTransform tNext;
	};

	static class FrontTransformList
	{
		FrontLinkedTransform tFirst, tLast;
		int tLength;

		FrontTransformList()
		{
			tFirst = null;
			tLast = null;
			tLength = 0;
		}
	};

	/***************************************************/
	/****** Members ************************************/
	/***************************************************/
	
	/** current symbol being defined */		private Symbol      currentFrontSymbol;
	
	/** place to save layer during def */	private Layer           backupLayer;
	
	/** symbol has been named */			private boolean          symbolNamed;
	/** flag for error encountered */		private boolean          errorFound;
	/** definition in progress flag */		private boolean          isInCellDefinition;
	/** end command flag */					private boolean          endIsSeen;
	/** number of chars in buffer */		private int              charactersRead;
	/** flag to reset buffer */				private boolean          resetInputBuffer;
	/** number of "fatal" errors */			private int              numFatalErrors;
	/** null layer errors encountered */	private boolean          numNullLayerErrors;
	/** ignore statements until DF */		private boolean          ignoreStatements;
	/** 91 pending */						private boolean          namePending;
	
	/** current layer */					private Layer			 currentLayer;					
	
	/** symbol table */						private HashMap<Integer,Symbol> symbolTable;
	/** lookahead character */				private int              nextInputCharacter;

	/** map from layer names to layers */	private HashMap<String,Layer> cifELayerNames;
	/** the line being read */				private StringBuffer     inputBuffer;
	
	
	/** Name of the file being input. */					private String filePath;
	/** The raw input stream. */							private InputStream inputStream;
	/** The line number reader (text only). */				private LineNumberReader lineReader;
	private LinkedList<CRectangle> m_recsList;
	private long m_boxID;

	
	/***************************************************/
	/****** Cot'r **************************************/
	/***************************************************/
	public CIFImporter(URL inputFile, LinkedList<CRectangle> recsList)
	{
		m_boxID = 0;
		m_recsList = recsList;
		readInputFile(inputFile);
	}
	
	
	/***************************************************/
	/****** Public methods *****************************/
	/***************************************************/
	public boolean Parse()
	{
		// initialize cif layers
		if(initCIFLayers()) return true;

		// parse the cif and create a listing
		if (interpret()) return true;

		// clean up
		doneInterpreter();

		return false;
	}
	
	
	
	/***************************************************/
	/****** Utils methods *****************************/
	/***************************************************/
	
	private boolean interpret()
	{
		initParser();
		initInterpreter();
		inFromFile();
		parseFile();
		doneParser();

		if (numFatalErrors > 0) 
			return true;

		return false;
	}

	/**
	 * Initialize CIF layers hash maps
	 */
	private boolean initCIFLayers()
	{
		cifELayerNames = new HashMap<String,Layer>();		
		
		for (Layer elayer : Layer.values()) 
		{
			String layerName = elayer.toString();
			
			if(!layerName.equals(Layer.None.toString()))
				cifELayerNames.put(layerName, elayer);
		}

		return false;
	}

	private void initInterpreter()
	{
		numNullLayerErrors = false;
		isInCellDefinition = false;
		ignoreStatements = false;
		namePending = false;
		currentLayer = Layer.None;
		initUtilities();
	}

	private void initParser()
	{
		errorFound = false;
		isInCellDefinition = false;
		endIsSeen = false;
		initInput();
		initErrors();
	}

	private void doneParser()
	{
		if (!endIsSeen) 
			printMessage("missing End command");
	}

	/**
	 * CIF parser main entry point
	 * @return
	 */
	private int parseFile()
	{
		int comCount = 1;
		for(;;)
		{
			int com = parseStatement();
			if (com == END || com == ENDFILE) break;
			comCount++;
		}
		return comCount;
	}

	private void doneInterpreter()
	{
		if (numNullLayerErrors)
		{
			System.err.println("Warning: some CIF objects were not read");
		}
	}


	private void initInput()
	{
		charactersRead = 0;
		resetInputBuffer = true;
	}

	private void initErrors()
	{
		numFatalErrors = 0;
	}

	private void initUtilities()
	{
		symbolTable = new HashMap<Integer,Symbol>();
	}

	private void inFromFile()
	{
		try
		{
			nextInputCharacter = lineReader.read();
		} catch (IOException e)
		{
			nextInputCharacter = -1;
		}
	}

	/**
	 * Get next character from input to private member
	 * @return next input character
	 */
	private char getNextCharacter()
	{
		if (resetInputBuffer)
		{
			resetInputBuffer = false;
			inputBuffer = new StringBuffer();
			charactersRead = 0;
		}

		int c = nextInputCharacter;
		if (c >= 0)
		{
			if (c != '\n')
			{
				charactersRead++;
				inputBuffer.append((char)c);
			} else resetInputBuffer = true;
			try
			{
				nextInputCharacter = lineReader.read();
			} catch (IOException e)
			{
				nextInputCharacter = -1;
			}
		}
		return (char)c;
	}

	private char peekNextCharacter()
	{
		return (char)nextInputCharacter;
	}

	private boolean atEndOfFile()
	{
		return nextInputCharacter < 0;
	}

	private void skipBlanks()
	{
		for(;;)
		{
			if (atEndOfFile()) break;
			int c = peekNextCharacter();
			if (CommonUtils.isDigit((char)c) || Character.isUpperCase((char)c)) break;
			if (c == '(' || c == ')' || c == ';' || c == '-') break;
			getNextCharacter();
		}
	}

	/**
	 * CIF parser statement reader
	 * @return
	 */
	private int parseStatement()
	{
		if (atEndOfFile()) return ENDFILE;

		skipBlanks();		// flush initial junk

		int curChar = getNextCharacter();
		int command = 0;
		int xRotate=0, yRotate=0, length=0, width=0, diameter=0, symbolNumber=0, multiplier=0, divisor=0, userCommand=0;
		Point center = null, namePoint = null;
		FrontTransformList curTList = null;
		CPolygon curPoly = null;
		String lName = null, nameText = null, userText = null;
		switch (curChar)
		{
			/***********************************************************************************************
			 * The POLYGON statement takes a series of coordinate pairs and draws a filled polygon from them. 
			 * Since filled polygons must be closed, the first and last coordinate points 
			 * are implicitly connected and need not be the same. 
			 * Polygons can be arbitrarily complex, including concavity and self-intersection.
			 */
			case 'P':
				command = POLYCOM;
				curPoly = new CPolygon();
				getPoly(curPoly);
				
				if (errorFound)
					return printMessage("Error found");
				break;
			
			/***********************************************************************************************
			 * The BOX statement is the most commonly used way of specifying geometry.
			 * It describes a rectangle by giving its length, width, center position, and an optional rotation. 
			 * 
			 * The format is as follows:    
			 * 			B [length] [width] [xpos] [ypos] [rotation] ;
			 * 
			 * Without the rotation field, the four numbers specify a box the center of which is at (xpos, ypos) 
			 * and is length across in x and width tall in y. 
			 * All numbers in CIF are integers that refer to centimicrons of distance, 
			 * unless subroutine scaling is specified. 
			 * The optional rotation field contains two numbers that define a vector endpoint starting 
			 * at the origin. The default value of this field is (1, 0), which is a right-pointing vector. 
			 * Thus the rotation clause 10 5 defines a 30-degree counterclockwise rotation from the normal. 
			 * Similarly, 10 -10 will rotate clockwise by 45 degrees. 
			 * Note that the magnitude of this rotation vector has no meaning.
			 */
			case 'B':
				command = BOXCOM;
				xRotate = 1; yRotate = 0;
				length = getNumber(); if (errorFound) return printMessage("Error found");
				width = getNumber(); if (errorFound) return printMessage("Error found");
				center = getPoint(); if (errorFound) return printMessage("Error found");
				skipSeparators();
				if (((curChar = peekNextCharacter()) >= '0' && curChar <= '9') || curChar == '-')
				{
					xRotate = getSignedInteger(); if (errorFound) return printMessage("Error found");
					yRotate = getSignedInteger(); if (errorFound) return printMessage("Error found");
				}
				break;

			/***********************************************************************************************
			 * The ROUNDFLASH statement, draws a filled circle, given the diameter and the center coordinate. 
			 * For example, the statement:
			 *     R 20 30 40;
			 * represents a circle that has a radius of 10 (diameter of 20), centered at (30, 40).
			 */
			case 'R':
				command = FLASHCOM;
				
				diameter = getNumber(); 
				if (errorFound) 
					return printMessage("Error found");
				
				center = getPoint(); 
				if (errorFound) 
					return printMessage("Error found");
				
				break;

			/***********************************************************************************************
			 * The WIRE statement is used to construct a path that runs between a set of points. 
			 * The path can have a nonzero width and has rounded corners. 
			 * After the WIRE keyword comes the width value and then an arbitrary number of coordinate pairs 
			 * that describe the endpoints. 
			 */				
			case 'W':
				command = WIRECOM;
				
				width = getNumber(); 
				if (errorFound) 
					return printMessage("Error found");
		
				curPoly = new CPolygon();
				getPoly(curPoly);

				if (errorFound) 
					return printMessage("Error found");
				
				break;

			/***********************************************************************************************
			 * The LAYER statement sets the mask layer to be used for all subsequent geometry 
			 * until the next such statement. 
			 * Following the LAYER keyword comes a single layer-name parameter. 
			 * 
			 * For example, the command:
			 *     L NC;
			 * sets the layer to be the nMOS contact cut.
			 * 
			 * Layer's name can contain latters, digits and '_' only
			 */
			case 'L':
				command = LAYER;
				
				skipBlanks();
				StringBuffer layerName = new StringBuffer();
				int chr = peekNextCharacter();
				
				while(chr == '_' || Character.isLetter(chr) || CommonUtils.isDigit((char)chr))
				{
					layerName.append(getNextCharacter());
					chr = peekNextCharacter();
				}
				
				if (layerName.length() == 0) 
				{
					errorFound = true; 
					return printMessage("Error: No layer");
				}
				
				lName = layerName.toString();
				
				break;

			/***********************************************************************************************
			 * Defining subroutines for use in a CALL statement. 
			 * The statements to be packaged are enclosed between DS (definition start) 
			 * and DF (definition finish) statements. 
			 * Arguments to the DS statement are the subroutine number and a subroutine scaling factor. 
			 * There are no arguments to the DF statement. 
			 * The scaling factor for a subroutine consists of a numerator followed by a denominator 
			 * that will be applied to all values inside the subroutine. 
			 * This scaling allows large numbers to be expressed with fewer digits 
			 * and allows ease of rescaling a design. 
			 * The scale factor cannot be changed for each invocation of the subroutine since 
			 * it is applied to the definition. 
			 * 
			 * CIF subroutines can be overwritten by deleting them and then redefining them. 
			 * The DD statement (delete definition) takes a single parameter and deletes every 
			 * subroutine that has a number greater than or equal to this value. 
			 * The statement is useful when merging multiple CIF files because designs can be 
			 * defined, invoked, and deleted without causing naming conflicts. 
			 */
			case 'D':
				skipBlanks();
				
				switch (getNextCharacter())
				{
					case 'S':
						command = DEFSTART;
						
						symbolNumber = getNumber(); 
						if (errorFound) 
							return printMessage("Error found");
						
						skipSeparators(); 
						multiplier = divisor = 1;
						
						if (CommonUtils.isDigit(peekNextCharacter()))
						{
							multiplier = getNumber(); 
							if (errorFound) 
								return printMessage("Error found");
							
							divisor = getNumber(); 
							if (errorFound) 
								return printMessage("Error found");
						}
						if (isInCellDefinition)
						{
							errorFound = true;
							return printMessage("Error: nested definition");
						}
						isInCellDefinition = true;
						break;
						
					case 'F':
						command = DEFEND;
						
						if (!isInCellDefinition)
						{
							errorFound = true;
							return printMessage("Error: no definition start");
						}
						
						isInCellDefinition = false;
						break;
						
					case 'D':
						command = DELETEDEF;
						
						symbolNumber = getNumber(); 
						if (errorFound) 
							return printMessage("Error found");
						
						if (isInCellDefinition)
						{
							errorFound = true;
							return printMessage("Error: nested DD");
						}
						break;
						
					default:
						errorFound = true;
						return printMessage("Error: bad def");
				}
				break;

			/***********************************************************************************************
			 * The CALL statement invokes a collection of other statements that have been packaged with DS and DF. 
			 * All subroutines are given numbers when they are defined and these numbers 
			 * are used in the CALL to identify them. 
			 * If, for example, a LAYER statement and a BOX statement are packaged into subroutine 4, 
			 * then the statement:
			 *     C 4;
			 * will cause the box to be drawn on that layer.
			 * In addition to simply invoking the subroutine, a CALL statement can include transformations 
			 * to affect the geometry inside the subroutine. 
			 * Three transformations can be applied to a subroutine in CIF: 
			 * translation, rotation, and mirroring. 
			 * Translation is specified as the letter T followed by an x, y offset. 
			 * 	These offsets will be added to all coordinates in the subroutine, 
			 * 	to translate its graphics across the mask. 
			 * Rotation is specified as the letter R followed by an x, y vector endpoint that, 
			 * 	much like the rotation clause in the BOX statement, defines a line to the origin. 
			 * 	The unrotated line has the endpoint (1, 0), which points to the right. 
			 * Mirroring is available in two forms: MX to mirror in x and MY to mirror in y. 
			 * Mirroring is a bit confusing, because MX causes a negation of the x coordinate, 
			 * which effectively mirrors about the y axis! 
			 */
			case 'C':
				command = CALLCOM;
				
				symbolNumber = getNumber(); 
				if (errorFound) 
					return printMessage("Error found");
				
				skipBlanks();
				curTList = new FrontTransformList();
				
				for(;;)
				{
					TransformData trans = new TransformData();
					int val = peekNextCharacter();
					if (val == ';') break;
					switch (peekNextCharacter())
					{
						case 'T':
							getNextCharacter();
							trans.kind = TRANSLATE;
							trans.xt = getSignedInteger(); if (errorFound) return printMessage("Error found");
							trans.yt = getSignedInteger(); if (errorFound) return printMessage("Error found");
							break;

						case 'M':
							trans.kind = MIRROR;
							getNextCharacter(); skipBlanks();
							switch (getNextCharacter())
							{
								case 'X': trans.xCoord = true; break;
								case 'Y': trans.xCoord = false; break;
								default:  errorFound = true; return printMessage("Error: bad axis");
							}
							break;

						case 'R':
							trans.kind = ROTATE;
							getNextCharacter();
							trans.xRot = getSignedInteger(); if (errorFound) return printMessage("Error found");
							trans.yRot = getSignedInteger(); if (errorFound) return printMessage("Error found");
							break;

						default:
							errorFound = true; return printMessage("Error: bad transition");
					}
					skipBlanks();		// between transformation commands
				}	// end of while (1) loop
				break;

			/***********************************************************************************************
			 * Comment with arbitrary text
			 */
			case '(':
				{
					int level = 1;
					command = COMMENT;
					
					StringBuffer comment = new StringBuffer();
					while (level != 0)
					{
						curChar = getNextCharacter();
						switch (curChar)
						{
							case '(':
								level++;
								comment.append('(');
								break;
							case ')':
								level--;
								if (level != 0) comment.append(')');
								break;
							case -1:
								errorFound = true; return printMessage("Error: bad comment");
							default:
								comment.append(curChar);
						}
					}
				}
				break;

			/***********************************************************************************************
			 * End marker
			 */
			case 'E':
				skipBlanks();
				if (isInCellDefinition)
				{
					errorFound = true;
					return printMessage("Error: nested end");
				}
				if (!atEndOfFile()) printMessage("more text follows end command");
				endIsSeen = true;
				processEnd();
				return END;

			/***********************************************************************************************
			 * Single command terminator
			 */
			case ';':
				return NULLCOMMAND;

			default:
				if (CommonUtils.isDigit((char)curChar))
				{
					userCommand = curChar - '0';
					
					if (userCommand == 9)
					{
						curChar = peekNextCharacter();
						if (curChar == ' ' || curChar == '\t' || curChar == '1' || curChar == '2' || curChar == '3')
						{
							switch (getNextCharacter())
							{
								case ' ':
								case '\t':
									skipSpaces(); nameText = parseName(); if (errorFound) return printMessage("Error found");
									command = SYMNAME;
									break;
								case '1':
								case '2':
								case '3':
									if (!skipSpaces())
									{
										return printMessage("Error: no space");
									}
									nameText = parseName(); if (errorFound) return printMessage("Error found");
									switch (curChar)
									{
										case '1':
											command = INSTNAME;
											break;
										case '2':
										{
											command = GEONAME;
											namePoint = getPoint(); if (errorFound) return printMessage("Error found");
											skipBlanks();
											StringBuffer layName = new StringBuffer();
											for (int i = 0; i<4; i++)
											{
												int ch = peekNextCharacter();
												if (!Character.isUpperCase((char)ch) && !CommonUtils.isDigit((char)ch)) break;
												layName.append(getNextCharacter());
											}
											lName = layName.toString();
											break;
										}
										case '3':
											command = LABELCOM;
											namePoint = getPoint(); if (errorFound) return printMessage("Error found");
											break;
									}
									break;
							}
						} else
						{
							command = USERS;
							userText = getUserText();
							if (atEndOfFile())
							{
								errorFound = true; return printMessage("Error: bad user");
							}
						}
					}
				} else
				{
					errorFound = true;
					return printMessage("Error: bad command");
				}
		}

		// by now we have a syntactically valid command although it might be missing a semi-colon
		switch (command)
		{
			case WIRECOM:
				makeWire(width, curPoly);
				break;
			case DEFSTART:
				makeStartDefinition(symbolNumber, multiplier, divisor);
				break;
			case DEFEND:
				makeEndDefinition();
				break;
			case DELETEDEF:
				makeDeleteDefinition(symbolNumber);
				break;
			case CALLCOM:
				makeCall(symbolNumber, curTList);
				break;
			case LAYER:
				makeLayer(lName);
				break;
			case FLASHCOM:
				makeFlash(diameter, center);
				break;
			case POLYCOM:
				makePolygon(curPoly);
				break;
			case BOXCOM:
				makeBox(length, width, center, xRotate, yRotate);
				break;
			case COMMENT:
				break;
			case USERS:
				makeUserComment(userCommand, userText);
				break;
			case SYMNAME:
				makeSymbolName(nameText);
				break;
			case INSTNAME:
				makeInstanceName(nameText);
				break;
			case GEONAME:
				makeGeomName(nameText, namePoint, lName);
				break;
			case LABELCOM:
				makeLabel(nameText, namePoint);
				break;
			default:
				errorFound = true;
				return printMessage("Error found");
		}
		if (!skipSemicolon()) {errorFound = true; return printMessage("Error: no semi");}

		return command;
	}

	private void makeLabel(String name, Point pt)
	{
		if (ignoreStatements) return;
		if (name.length() == 0)
		{
			printMessage("null label ignored");
			return;
		}

		/* TODO: implement */
	}

	private void makeGeomName(String name, Point pt, String layer)
	{
		if (ignoreStatements) return;
		if (name.length() == 0)
		{
			printMessage("null geometry name ignored");
			return;
		}
		
		/* TODO: implement */
	}

	private void makeSymbolName(String name)
	{
		if (ignoreStatements) return;
		if (!isInCellDefinition)
		{
			printMessage("no symbol to name");
			return;
		}
		if (name.length() == 0)
		{
			printMessage("null symbol name ignored");
			return;
		}
		if (symbolNamed)
		{
			printMessage("symbol is already named, new name ignored");
			return;
		}
		symbolNamed = true;
		currentFrontSymbol.name = name;
	}

	private void makeUserComment(int command, String text)
	{
		if (ignoreStatements) return;
	}

	private void makeBox(int length, int width, Point center, int xr, int yr)
	{
		if (ignoreStatements) return;

		if (currentLayer == Layer.None)
		{
			numNullLayerErrors = true;
			return;
		}
		if (length == 0 || width == 0)
		{
			printMessage("box with null length or width specified, ignored");
			return;
		}

		m_recsList.add(
				new CRectangle(
						center.x - width/2,
						center.y - length/2,
						center.x + width/2,
						center.y + length/2,
						currentLayer,
						m_boxID++
						)
				);
	}

	private void makeFlash(int diameter, Point center)
	{
		if (ignoreStatements) 
			return;
		
		if (currentLayer == Layer.None)
		{
			numNullLayerErrors = true;
			return;
		}
		
		if (diameter == 0)
		{
			printMessage("flash with null diamter, ignored");
			return;
		}

		/* TODO: Make a filled circle, given the diameter and the center coordinate */
		printMessage("Filled circle is not supported (ignored)");
	}

	private void makeLayer(String lName)
	{
		if (ignoreStatements) 
			return;
		
		lName = MapInputToDefinedLayer(lName);
		
		Layer layer = cifELayerNames.get(lName);
		
		if(layer != null)
			currentLayer = layer; 
		else
			printMessage("Bad layer name: " + lName + ". Ignored.");
	}

	private void makeCall(int symbol, FrontTransformList list)
	{
		if (ignoreStatements) 
			return;
		
		int j = getFrontTransformListLength(list);

		for (int i = 1; i <=j; i++)
		{
			// build up incremental transformations
			TransformData temp = removeFrontTransformEntry(list);
			if (temp.kind == MIRROR)
			{
				mirrorMatrix(temp.xCoord);
			} 
			else if (temp.kind == TRANSLATE)
			{
				translateMatrix(temp.xt, temp.yt);
			} 
			else if (temp.kind == ROTATE)
			{
				rotateMatrix(temp.xRot, temp.yRot);
			} 
			else
			{
				printMessage("interpreter: no such transformation");
			}
			
		}

		/* TODO: Make call */
		printMessage("Call statment is not supported (ignored)");
	}

	private void rotateMatrix(int xRot, int yRot)
	{
		if (yRot == 0 && xRot >= 0) 
			return;
		
		/* TODO: implement */
	}

	private void translateMatrix(int xtrans, int ytrans)
	{
		/* TODO: implement */
	}

	private void mirrorMatrix(boolean xCoord)
	{
		/* TODO: implement */
	}

	private int getFrontTransformListLength(FrontTransformList a)
	{
		if (a == null) return 0;
		return a.tLength;
	}

	private TransformData removeFrontTransformEntry(FrontTransformList a)
	{
		if (a.tFirst == null)
		{
			// added extra code to initialize "ans" to a dummy value
			TransformData ans = new TransformData();
			ans.kind = TRANSLATE;
			ans.xt = ans.yt = 0;
			return ans;
		}
		FrontLinkedTransform temp = a.tFirst.tNext;
		TransformData ans = a.tFirst.tValue;
		a.tFirst = temp;
		if (a.tFirst == null) a.tLast = null;
		a.tLength -= 1;
		return ans;
	}

	private void makeDeleteDefinition(int n)
	{		
		/* TODO: Implement definition deletion */
		printMessage("DD not supported (ignored)");
	}

	private void makeEndDefinition()
	{
		if (ignoreStatements)
		{
			ignoreStatements = false;
			return;
		}
		isInCellDefinition = false;
		
		currentLayer = backupLayer;		// restore old layer
		
		if (!symbolNamed)
		{
			String s = "SYM" + currentFrontSymbol.symNumber;
			currentFrontSymbol.name = s;
		}
		currentFrontSymbol.defined = true;
	}

	private void makeStartDefinition(int symbol, int mtl, int div)
	{
		currentFrontSymbol = lookupSymbol(symbol);
		if (currentFrontSymbol.defined)
		{
			// redefining this symbol
			String mess = "attempt to redefine symbol " + symbol + " (ignored)";
			printMessage(mess);
			ignoreStatements = true;
			return;
		}

		isInCellDefinition = true;
		backupLayer = currentLayer;	// save current layer
		currentLayer = Layer.None;
		
		symbolNamed = false;					// symbol not named
	}

	private void makeWire(int width, CPolygon poly)
	{
		
		if (ignoreStatements) 
			return;
		if (currentLayer == Layer.None)
		{
			numNullLayerErrors = true;
			return;
		}
		
		/*
		 * TODO: Make a path that runs between a set of points. 
		 * 	The path can have a nonzero width and has rounded corners.
		 */
		printMessage("Wire is not supported (ignored)");
	}

	/**
	 * Get input text before ';'
	 * @return input text
	 */
	private String getUserText()
	{
		StringBuffer user = new StringBuffer();
		for(;;)
		{
			if (atEndOfFile()) break;
			if (peekNextCharacter() == ';') break;
			user.append(getNextCharacter());
		}
		return user.toString();
	}

	/**
	 * Parse input a word in a time
	 * @return next parsed word from the input
	 */
	private String parseName()
	{
		StringBuffer nText = new StringBuffer();
		for(;;)
		{
			if (atEndOfFile()) break;
			int c = peekNextCharacter();
			if (c == ';' || c == ' ' || c == '\t' || c == '{' || c == '}') break;
			getNextCharacter();
			nText.append((char)c);
		}
		return nText.toString();
	}

	private int getNumber()
	{
		boolean somedigit = false;
		int ans = 0;
		skipSpaces();

		while (ans < BIGSIGNED && CommonUtils.isDigit(peekNextCharacter()))
		{
			ans *= 10; ans += getNextCharacter() - '0';
			somedigit = true;
		}

		if (!somedigit)
		{
			return 0;
		}
		if (CommonUtils.isDigit(peekNextCharacter()))
		{
			return 0XFFFFFFFF;
		}
		return ans;
	}

	private boolean skipSemicolon()
	{
		boolean ans = false;
		skipBlanks();
		if (peekNextCharacter() == ';') { getNextCharacter(); ans = true; skipBlanks(); }
		return ans;
	}

	private boolean skipSpaces()
	{
		boolean ans = false;
		for(;;)
		{
			int c = peekNextCharacter();
			if (c != ' ' && c != '\t') break;
			getNextCharacter();
			ans = true;
		}
		return ans;
	}

	private void makePolygon(CPolygon polygon)
	{
		int length = polygon.getPointsCount();
		
		if (ignoreStatements) 
			return;

		if (currentLayer == Layer.None)
		{
			numNullLayerErrors = true;
			return;
		}
		if (length < 3)
		{
			printMessage("polygon with < 3 pts in path, ignored");
			return;
		}
		
		/*
		 * TODO:
		 * Make boxes from polygon's points linked list
		 */
	}
	


	/**
	 * Method to find a given symbol.
	 * If none, make a blank entry.
	 * @return a pointer to whichever.
	 */
	private Symbol lookupSymbol(int sym)
	{
		Symbol val = symbolTable.get(new Integer(sym));
		if (val == null)
		{
			// create a new entry
			val = new Symbol(sym);
			symbolTable.put(new Integer(sym), val);
		}
		return val;
	}


	private void makeInstanceName(String name)
	{
		if (ignoreStatements) return;
		if (name.length() == 0)
		{
			printMessage("null instance name ignored");
			return;
		}
		if (namePending)
		{
			printMessage("there is already a name pending, new name replaces it");
		}
		namePending = true;
	}

	private void processEnd()
	{
		if (namePending)
		{
			printMessage("no instance to match name command");
			namePending = false;
		}
	}

	private int getSignedInteger()
	{
		boolean sign = false;
		int ans = 0;
		skipSeparators();

		if (peekNextCharacter() == '-') { sign = true;   getNextCharacter(); }

		boolean someDigit = false;
		while (ans < BIGSIGNED && CommonUtils.isDigit(peekNextCharacter()))
		{
			ans *= 10; ans += getNextCharacter() - '0';
			someDigit = true;
		}

		if (!someDigit) { return 0; }
		if (CommonUtils.isDigit(peekNextCharacter()))
		{
			return sign ? -0X7FFFFFFF : 0X7FFFFFFF;
		}
		return sign ? -ans : ans;
	}

	private Point getPoint()
	{
		int x = getSignedInteger();
		int y = getSignedInteger();
		return new Point(x, y);
	}

	private void getPoly(CPolygon poly)
	{
		skipSeparators();
		
		for(;;)
		{
			int c = peekNextCharacter();
			if (!CommonUtils.isDigit((char)c) && c != '-') 
				break;
			
			Point temp = getPoint();	  
			if (errorFound) 
				break;
			
			poly.getPointsList().addLast(temp);
			skipSeparators();
		}
	}

	private void skipSeparators()
	{
		for(;;)
		{
			int c = peekNextCharacter();
			switch (c)
			{
				case '(':
				case ')':
				case ';':
				case '-':
				case -1:
					return;
				default:
					if (CommonUtils.isDigit((char)c)) return;
					getNextCharacter();
			}
		}
	}
	
    private boolean readInputFile(URL fileURL)
	{
		filePath = fileURL.getFile();

        try
		{
			URLConnection urlCon = fileURL.openConnection();
            urlCon.setConnectTimeout(10000);
            urlCon.setReadTimeout(1000);

			inputStream = urlCon.getInputStream();
		} 
        catch (IOException e)
		{
			System.out.println("Could not find file: " + filePath);
			return true;
		}
		
        InputStreamReader is = new InputStreamReader(inputStream);
        lineReader = new LineNumberReader(is);
        
        return false;
	}	
    
    private static int printMessage(String msg)
    {
        System.out.println("Layout importer warning: " + msg);
        
        return 1;
    }    
    
    private String MapInputToDefinedLayer(String LayerName)
    {
    	if (LayerName.equals("CWP")) 
    		return Layer.Pwel.toString();
    	if (LayerName.equals("CWN")) 
    		return Layer.Nwel.toString();
       	if(LayerName.equals("CAA"))
    		return Layer.Active.toString();
    	else if(LayerName.equals("CCC"))
    		return Layer.Contact.toString();
    	else if(LayerName.equals("CMF"))
    		return Layer.Metal1.toString();
    	else if(LayerName.equals("CMS"))
    		return Layer.Metal2.toString();
    	else if(LayerName.equals("CPG"))
    		return Layer.Poly.toString();
    	else if(LayerName.equals("CSN"))
    		return Layer.N_diff.toString();
    	else if(LayerName.equals("CSP"))
    		return Layer.P_diff.toString();
    	else if(LayerName.equals("CVA"))
    		return Layer.Via1_2.toString();
    	else if(LayerName.equals("CVS"))
    		return Layer.Via2_3.toString();
    	else return LayerName;
    }
}
